package solutions.ferpa.ztest.intf;

import br.com.telescope.adsrt.dom.DomBigInteger;
import br.com.telescope.adsrt.dom.DomFlag;
import br.com.telescope.adsrt.dom.DomId;
import br.com.telescope.adsrt.dom.DomInteger;
import br.com.telescope.adsrt.dom.DomTextLong;
import br.com.telescope.adsrt.dom.DomUpper;
import br.com.telescope.adsrt.lib.*;
import br.com.telescope.sequence.SequenceManager;
import br.com.telescope.t2.ServiceModel;
import br.com.telescope.t2.exception.ApplicationRuntimeException;
import br.com.telescope.t2.feature.CustomActionFeature;
import br.com.telescope.t2.feature.DeleteFeature;
import br.com.telescope.t2.feature.ExportFeature;
import br.com.telescope.t2.feature.ImportFeature;
import br.com.telescope.t2.feature.InsertFeature;
import br.com.telescope.t2.feature.ListFeature;
import br.com.telescope.t2.feature.QueryFeature;
import br.com.telescope.t2.feature.RecordActionFeature;
import br.com.telescope.t2.feature.UpdateFeature;
import br.com.telescope.t2.feature.ViewFeature;
import br.com.telescope.util.JdbcUtil;
import solutions.ferpa.ztest.ent.*;

/**
 * Cadastro de sequencias.
 * <p>
 * Interface para manutenção das sequencias que poderão ser utilizadas 
 * pelo sistema.
 * <P>
 * 
 * <P>
 * Sempre que um componente do sistema tentar consumir uma sequencia que 
 * ainda não foi criada, o gerenciador cria esta sequencia 
 * automaticamente. Sequencias criadas automaticamente não tem cache 
 * (atributo cache é preenchido com "1") e não são replicadas (o que pode 
 * ser um problema para o caso da respectiva tabela estar configurada 
 * para replicação).
 * <P>
 * 
 * <P>
 * ATENÇÃO:
 * <P>
 * * MUITO CUIDADO AO ALTERAR UMA SEQUENCIA EM AMBIENTE QUE ESTEJA EM USO 
 * POIS ISSO PODE CAUSAR COMPORTAMENTOS INESPERADOS COM O CONSUMO DAS 
 * MESMAS.
 * <P>
 * * SUGERE-SE QUE ESTE TIPO DE MANUTENÇÃO SEJA FEITO COM O SISTEMA 
 * OFFLINE
 * <P>
 * * SUGERE-SE REINICIAR O SERVIDOR APÓS ALTERAÇÕES NAS SEQUENCIAS PARA 
 * GARANTIR QUE NENHUM CACHE VÁ GERAR ALGUM COMPORTAMENTO ANORMAL NO 
 * CONSUMO DE SEQUENCIAS OU REPLICAÇÃO DE DADOS.
 * <P>
 * Telescope origin: SEQUENCE.CAD_SEQUENCES
 * <P>
 * Base entity: SEQUENCE.SEQUENCES
 * <P>
 * @author TELESCOPE - Build task T2 Web Interface Generator
 * <P>
 * @version 0.1
 */
public class CadSequences extends ServiceModel {
    
    // <editor-fold defaultstate="collapsed" desc="Generated Code">
    public static final String GLOBAL_NAME = "SEQUENCE.CAD_SEQUENCES";
    
    @Override
    public String globalName() {
        return GLOBAL_NAME;
    }
    
    {
        setTitle("Cadastro de sequencias");
    }
    
    protected EntSequences base;
    protected EntSequences rec; // NEED_TJW1_COMPATIBILITY
    
    // Record items
    private DomUpper aSequence;
    private DomFlag aRandom;
    private DomTextLong aAttributes;
    private DomBigInteger aNextValue;
    private DomBigInteger aMaxValue;
    private DomInteger aCache;
    private DomBigInteger aNextRangeMinValue;
    private DomBigInteger aNextRangeMaxValue;
    private DomInteger aRangeSize;
    private DomTextLong aDescription;
    private DomId aId;
    private DomBigInteger aCurrent;
    private DomBigInteger aNumerosDisponiveis;
    private DomBigInteger aNumeroInicial;
    private DomBigInteger aNumeroFinal;
    
    // Query items
    private DomUpper qSequence;
    
    @Override
    public String getBaseSql() {
        return "select BASE.*"
                + " from SEQUENCES BASE";
    }
    
    @Override
    public void initAttributes() {
        
        aSequence = new DomUpper();
        aSequence.context(ctx).name("SEQUENCE").label("Sequence");
        
        aRandom = new DomFlag();
        aRandom.context(ctx).name("RANDOM").label("Random");
        aRandom.setDefaultValue("N");
        
        aAttributes = new DomTextLong();
        aAttributes.context(ctx).name("ATTRIBUTES").label("Attributes");
        
        aNextValue = new DomBigInteger();
        aNextValue.context(ctx).name("NEXT_VALUE").label("Next value");
        aNextValue.setDefaultValue("1");
        
        aMaxValue = new DomBigInteger();
        aMaxValue.context(ctx).name("MAX_VALUE").label("Max value");
        
        aCache = new DomInteger();
        aCache.context(ctx).name("CACHE").label("Cache");
        aCache.setDefaultValue("1");
        
        aNextRangeMinValue = new DomBigInteger();
        aNextRangeMinValue.context(ctx).name("NEXT_RANGE_MIN_VALUE").label("Next range min value");
        
        aNextRangeMaxValue = new DomBigInteger();
        aNextRangeMaxValue.context(ctx).name("NEXT_RANGE_MAX_VALUE").label("Next range max value");
        
        aRangeSize = new DomInteger();
        aRangeSize.context(ctx).name("RANGE_SIZE").label("Range size");
        
        aDescription = new DomTextLong();
        aDescription.context(ctx).name("DESCRIPTION").label("Description");
        
        aId = new DomId();
        aId.context(ctx).name("ID").label("Id");
        
        aCurrent = new DomBigInteger();
        aCurrent.context(ctx).name("CURRENT").label("Next value (mem)").unbound(true);
        
        aNumerosDisponiveis = new DomBigInteger();
        aNumerosDisponiveis.context(ctx).name("NUMEROS_DISPONIVEIS").label("Números disponíveis").unbound(true);
        
        aNumeroInicial = new DomBigInteger();
        aNumeroInicial.context(ctx).name("NUMERO_INICIAL").label("Número inicial").unbound(true);
        
        aNumeroFinal = new DomBigInteger();
        aNumeroFinal.context(ctx).name("NUMERO_FINAL").label("Número final").unbound(true);
        
        
        qSequence = new DomUpper();
        qSequence.context(ctx).name("SEQUENCE").label("Sequence");
    }
    
    @Override
    public String getQueryPkColumn() {
        return "BASE.ID";
    }
    
    @Override
    public void setId(String value) {
        this.aId.setValue(value);
    }
    
    @Override
    public EntSequences getRecord() {
        if (base == null) {
            base = new EntSequences(ctx);
            rec = base;
        }
        return this.base;
    }
    
    @Override
    public String getContextName() {
        return aSequence.toText();
    }
    
    @Override
    public String getDefaultOrderBy() {
        return "SEQUENCE, RANDOM, ATTRIBUTES, NEXT_VALUE";
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="QUERY">
    public class FQuery extends QueryFeature {
        
        @Override
        public void initForm() {
            addField(qSequence, "opt");
        }
        
        {
            setFeatureName("QUERY");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="LIST">
    public class FList extends ListFeature {
        
        @Override
        public void initForm() {
            addField(aSequence, "view");
            addField(aRandom, "view");
            addField(aAttributes, "view");
            addField(aCurrent, "view");
            addField(aNextValue, "view");
            addField(aMaxValue, "view");
            addField(aCache, "view");
            addField(aNextRangeMinValue, "view");
            addField(aNextRangeMaxValue, "view");
            addField(aRangeSize, "view");
        }
        
        @Override
        public void preRecord() {
            try {
                popularValorCorrente();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("LIST");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="INSERT">
    public class FInsert extends InsertFeature {
        
        @Override
        public void initForm() {
            aSequence.setOptional(false);
            addField(aSequence, "rqd");
            addField(aRandom, "opt");
            addField(aAttributes, "opt");
            aNextValue.setOptional(false);
            addField(aNextValue, "rqd");
            addField(aMaxValue, "opt");
            aCache.setOptional(false);
            addField(aCache, "rqd");
            addField(aNextRangeMinValue, "opt");
            addField(aNextRangeMaxValue, "opt");
            addField(aRangeSize, "opt");
            registerEvent("ON_CHANGE", "RANDOM", "tratarVisibilidadeCampos");
        }
        
        {
            setFeatureName("INSERT");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="UPDATE">
    public class FUpdate extends UpdateFeature {
        
        @Override
        public void initForm() {
            aSequence.setOptional(false);
            addField(aSequence, "rqd");
            addField(aRandom, "opt");
            addField(aAttributes, "opt");
            aNextValue.setOptional(false);
            addField(aNextValue, "rqd");
            addField(aMaxValue, "opt");
            aCache.setOptional(false);
            addField(aCache, "rqd");
            addField(aNextRangeMinValue, "opt");
            addField(aNextRangeMaxValue, "opt");
            addField(aRangeSize, "opt");
            aNumerosDisponiveis.setEditable(false);
            addField(aNumerosDisponiveis, "view");
            registerEvent("ON_CHANGE", "RANDOM", "tratarVisibilidadeCampos");
        }
        
        @Override
        public void preDisplay() {
            try {
                tratarVisibilidadeCampos();
                calcularNumerosDisponiveis();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        @Override
        public void posUpdate() {
            try {
                atualizarGerenciadoresMemoria();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("UPDATE");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="DELETE">
    public class FDelete extends DeleteFeature {
        
        @Override
        public void initForm() {
            addField(aSequence, "view");
        }
        
        {
            setFeatureName("DELETE");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="PESQUISA_BASE">
    public class FPesquisaBase extends RecordActionFeature {
        
        @Override
        public void initForm() {
        }
        
        @Override
        public void execute() {
            try {
                pesquisaBase();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("PESQUISA_BASE");
            setTitle("Pesquisar base");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="GERAR_NUMEROS">
    public class FGerarNumeros extends RecordActionFeature {
        
        @Override
        public void initForm() {
            aMaxValue.setEditable(false);
            addField(aMaxValue, "view");
            aNumeroInicial.setOptional(false);
            addField(aNumeroInicial, "rqd");
            aNumeroFinal.setOptional(false);
            addField(aNumeroFinal, "rqd");
        }
        
        @Override
        public void preDisplay() {
            try {
                calcularMaiorNumeroGerado();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        @Override
        public void execute() {
            try {
                gerarNumeros();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("GERAR_NUMEROS");
            setTitle("Gerar numeros");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="CONSUMIR">
    public class FConsumir extends CustomActionFeature {
        
        @Override
        public void initForm() {
        }
        
        @Override
        public void execute() {
            try {
                consumir();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        {
            setFeatureName("CONSUMIR");
            setTitle("Consumir");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="VIEW">
    public class FView extends ViewFeature {
        
        @Override
        public void initForm() {
            addField(aSequence, "view");
            addField(aRandom, "view");
            addField(aAttributes, "view");
            addField(aNextValue, "view");
            addField(aMaxValue, "view");
            addField(aCache, "view");
            addField(aNextRangeMinValue, "view");
            addField(aNextRangeMaxValue, "view");
            addField(aRangeSize, "view");
            addField(aNumerosDisponiveis, "view");
        }
        
        @Override
        public void preDisplay() {
            try {
                calcularNumerosDisponiveis();
                tratarVisibilidadeCampos();
            } catch (AdsException ex) {
                throw new ApplicationRuntimeException(ex);
            }
        }
        
        @Override
        public void onAction() {
            addSubmit(getFeature("CONSUMIR")).label("Consumir");
        }
        
        {
            setFeatureName("VIEW");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="IMPORT">
    public class FImport extends ImportFeature {
        
        @Override
        public void initForm() {
            addField(aSequence, "key");
            addField(aRandom, "opt");
            addField(aAttributes, "opt");
            aNextValue.setOptional(false);
            addField(aNextValue, "rqd");
            addField(aMaxValue, "opt");
            aCache.setOptional(false);
            addField(aCache, "rqd");
            addField(aNextRangeMinValue, "opt");
            addField(aNextRangeMaxValue, "opt");
            addField(aRangeSize, "opt");
        }
        
        {
            setFeatureName("IMPORT");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="EXPORT">
    public class FExport extends ExportFeature {
        
        @Override
        public void initForm() {
            addField(aSequence, "view");
            addField(aRandom, "view");
            addField(aAttributes, "view");
            addField(aNextValue, "view");
            addField(aMaxValue, "view");
            addField(aCache, "view");
            addField(aNextRangeMinValue, "view");
            addField(aNextRangeMaxValue, "view");
            addField(aRangeSize, "view");
        }
        
        {
            setFeatureName("EXPORT");
            setPrimary(false);
        }
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="initFeatures">
    
    @Override
    protected void initFeatures() {
        addFeature(new FQuery());
        addFeature(new FList());
        addFeature(new FInsert());
        addFeature(new FUpdate());
        addFeature(new FDelete());
        addFeature(new FPesquisaBase());
        addFeature(new FGerarNumeros());
        addFeature(new FConsumir());
        addFeature(new FView());
        addFeature(new FImport());
        addFeature(new FExport());
        super.initFeatures();
    }
    
    @Override
    public String getDefaultFeature() {
        return "QUERY";
    }
    // </editor-fold>
    
    // <editor-fold defaultstate="collapsed" desc="Getters and Setters">
    
    public String getSequence() {
        return aSequence.getValue();
    }
    
    public void setSequence(String value) {
        this.aSequence.setValue(value);
    }
    
    public String getRandom() {
        return aRandom.getValue();
    }
    
    public void setRandom(String value) {
        this.aRandom.setValue(value);
    }
    
    public String getAttributes() {
        return aAttributes.getValue();
    }
    
    public void setAttributes(String value) {
        this.aAttributes.setValue(value);
    }
    
    public Long getNextValue() {
        return aNextValue.getValue();
    }
    
    public void setNextValue(Long value) {
        this.aNextValue.setValue(value);
    }
    
    public Long getMaxValue() {
        return aMaxValue.getValue();
    }
    
    public void setMaxValue(Long value) {
        this.aMaxValue.setValue(value);
    }
    
    public Integer getCache() {
        return aCache.getValue();
    }
    
    public void setCache(Integer value) {
        this.aCache.setValue(value);
    }
    
    public Long getNextRangeMinValue() {
        return aNextRangeMinValue.getValue();
    }
    
    public void setNextRangeMinValue(Long value) {
        this.aNextRangeMinValue.setValue(value);
    }
    
    public Long getNextRangeMaxValue() {
        return aNextRangeMaxValue.getValue();
    }
    
    public void setNextRangeMaxValue(Long value) {
        this.aNextRangeMaxValue.setValue(value);
    }
    
    public Integer getRangeSize() {
        return aRangeSize.getValue();
    }
    
    public void setRangeSize(Integer value) {
        this.aRangeSize.setValue(value);
    }
    
    public String getDescription() {
        return aDescription.getValue();
    }
    
    public void setDescription(String value) {
        this.aDescription.setValue(value);
    }
    
    @Override
    public Long getId() {
        return aId.getValue();
    }
    
    public void setId(Long value) {
        this.aId.setValue(value);
    }
    
    public Long getCurrent() {
        return aCurrent.getValue();
    }
    
    public void setCurrent(Long value) {
        this.aCurrent.setValue(value);
    }
    
    public Long getNumerosDisponiveis() {
        return aNumerosDisponiveis.getValue();
    }
    
    public void setNumerosDisponiveis(Long value) {
        this.aNumerosDisponiveis.setValue(value);
    }
    
    public Long getNumeroInicial() {
        return aNumeroInicial.getValue();
    }
    
    public void setNumeroInicial(Long value) {
        this.aNumeroInicial.setValue(value);
    }
    
    public Long getNumeroFinal() {
        return aNumeroFinal.getValue();
    }
    
    public void setNumeroFinal(Long value) {
        this.aNumeroFinal.setValue(value);
    }
    public String getQuerySequence() {
        return qSequence.getQueryValue();
    }
    
    public void setQuerySequence(String value) {
        this.qSequence.setQueryValue(value);
    }
    // </editor-fold>
    
    /**
     * Tratar a visibilidade dos campos.
     * <p>
     * Para sequencias "Random", apresentar a faixa já consumida e a 
     * quantidade de números disponíveis. Para as demais sequencias, 
     * apresentar todos os outros campos.
     */
    public void tratarVisibilidadeCampos() throws AdsException {
        // <user-code id="11636705" parentId="11636692" hashcode="61957628" length="354">
        boolean whenRandom = "S".equals(aRandom.getValue());
        aAttributes.setHidden(whenRandom);
        aNextValue.setHidden(whenRandom);
        aMaxValue.setHidden(whenRandom);
        aCache.setHidden(whenRandom);
        aNextRangeMinValue.setHidden(whenRandom);
        aNextRangeMaxValue.setHidden(whenRandom);
        aRangeSize.setHidden(whenRandom);
        aNumerosDisponiveis.setVisible(whenRandom);
        // </user-code>
    }
    
    /**
     * Calcular números disponíveis.
     * <p>
     * Calcula a quantidade de números disponíveis. Para sequencias normais, 
     * corresponde a diferença entre o MAX e o próximo valor. Para sequencias 
     * aleatórias, corresponde a quantidade de números não consumidos.
     * <P>
     */
    public void calcularNumerosDisponiveis() throws AdsException {
        // <user-code id="11636984" parentId="11636980" hashcode="454ae3e9" length="497">
        // import br.com.telescope.util.JdbcUtil;
        if ("S".equals(aRandom.getValue())) {
            aNumerosDisponiveis.setValue(JdbcUtil.getFirst(Long.class, 
                    "select count(1) from sequence_numbers where sequence_id = ? and consumed = 'N'", 
                    aId.getValue()));
        } else {
            if (aMaxValue.isNotNull() && aNextValue.isNotNull()) {
                aNumerosDisponiveis.setValue(aMaxValue.getValue() - aNextValue.getValue() + 1);
            } else {
                aNumerosDisponiveis.nullify();
            }
        
        }
        // </user-code>
    }
    
    /**
     * Calcular maior número já usado.
     * <p>
     * Verifica o maior número já utilizado na sequencia.
     */
    public void calcularMaiorNumeroGerado() throws AdsException {
        // <user-code id="11637132" parentId="11637127" hashcode="-1f646643" length="162">
        aMaxValue.setValue(JdbcUtil.getFirst(
                Long.class, 
                "select max(number) from sequence_numbers where sequence_id = ?", 
                aId.getValue()));
        // </user-code>
    }
    
    /**
     * Pesquisar base.
     * <p>
     * Ao executar esta ação, o sistema verifica nos dados quais os números 
     * efetivamente utilizados.
     * <P>
     * Com isso, o administrador do sistema poderia reconfigurar a sequencia 
     * para ser reiniciada em algum valor específico, em especial, após 
     * cargas de dados legados contendo números já populados.
     */
    public void pesquisaBase() throws AdsException {
        // <user-code id="834297" parentId="834295" hashcode="779e0c58" length="80">
        ctx.p("<h2>Maior valor encontrado na base = " + rec.searchMaxValue() + "</h2>");
        // </user-code>
    }
    
    /**
     * Gerar numeros.
     * <p>
     * Gera números para sequencias aleatórias.
     */
    public void gerarNumeros() throws AdsException {
        // <user-code id="11636364" parentId="11636359" hashcode="689f6269" length="235">
        if (getRecord().valueOfRandom().equals("N")) {
            throw new AdsException("", AdsException.ERROR, "Somente sequencias aleatórias podem gerar números!");
        }
        getRecord().gerarNumeros(aNumeroInicial.getValue(), aNumeroFinal.getValue());
        // </user-code>
    }
    
    /**
     * Consumir.
     * <p>
     * Consome um número da sequencia a apresenta na tela.
     */
    public void consumir() throws AdsException {
        // <user-code id="11636367" parentId="11636366" hashcode="-18ca9026" length="98">
        ctx.readFields(aSequence);
        ctx.p("Numero: " + ctx.getNextVal(aSequence.getValue()));
        doView();
        
        // </user-code>
    }
    
    /**
     * Atualizar gerenciadores em memória.
     * <p>
     * Sinaliza os gerenciadores em memória de que a sequence foi alterada.
     * <P>
     */
    private void atualizarGerenciadoresMemoria() throws AdsException {
        // <user-code id="831629" parentId="831625" hashcode="-75709b20" length="74">
        ctx.getSequenceManager("default").reset(getRecord().valueOfSequence());
        
        
        // </user-code>
    }
    
    /**
     * Popular valor corrente.
     * <p>
     * Lê do SequenceManager em uso, qual o último valor utilizado. Este 
     * valor é lido do cache.
     * <P>
     */
    private void popularValorCorrente() throws AdsException {
        // <user-code id="831350" parentId="831343" hashcode="2b582385" length="189">
        // import br.com.telescope.sequence.SequenceManager;
        SequenceManager sm = ctx.getSequenceManager("default");
        aCurrent.setValue(sm.currentVal(aSequence.getValue(), ctx.getConnection()));
        
        // </user-code>
    }
    
    @Override
    public String getJsServiceFile() {
        return "ui/sequence/cad_sequences.js";
    }
}
